<template>
  <view :class="cls">
    <view :class="clsNav">
      <scroll-view
        scroll-x="true"
        :scroll-left="scrollLeft"
        scroll-with-animation
      >
        <view class="arco-tabs-nav-tab">
          <view
            v-for="item in sortedTabs"
            :key="item.key"
            :class="initTabClass(item)"
            @click="handleClick(item)"
          >
            {{ item.title }}
          </view>
          <tabsInk
            :active-tab-ref="activeTabRef"
            :direction="mergedDirection"
            :active-index="activeIndex"
          ></tabsInk>
        </view>
      </scroll-view>
      <view class="arco-tabs-nav-extra"> <slot name="extra"></slot> </view>
    </view>
    <view class="arco-tabs-content">
      <view :class="contentClass" :style="contStyle">
        <slot> </slot>
      </view>
    </view>
  </view>
</template>

<script lang="ts">
import {
  computed,
  CSSProperties,
  getCurrentInstance,
  nextTick,
  provide,
  reactive,
  ref,
  toRefs,
  watch,
} from "vue";
import { props } from "./props";
import { useSize } from "@/ArcoUni/hook/use-size";
import { tabsInjectionKey } from "./content";
import { isUndefined } from "@/utils/is";
import { TabData } from "./interface";
import tabsInk from "./tabs-nav-ink.vue";
import { elementSizeType } from "@/ArcoUni/hook/user-size-dom";
import { getRect } from "@/ArcoUni/utils/layout";
export default {
  name: "Tabs",
  options: {
    // 虚拟化节点
    virtualHost: true,
  },
  components: { tabsInk },
  props,
  emits: ["update:activeKey", "change", "tabClick", "add", "delete"],
  setup(props, { emit }) {
    const instance = getCurrentInstance() as any;

    const prefixCls = "arco-tabs";
    const { size } = toRefs(props);
    const { mergedSize } = useSize(size);

    const tabMap = reactive(new Map<number, TabData>());

    const tabsTabRef = ref("");
    const sortedTabs = computed(() => {
      const tabData: TabData[] = Array.from(tabMap.keys()).map(
        (key) => tabMap.get(key)!,
      );
      return tabData;
    });

    const scrollLeft = ref(0);

    const inkDom = ref<elementSizeType[]>([]);

    const activeTabRef = computed(() => {
      if (
        inkDom.value &&
        inkDom.value.length > 0 &&
        !isUndefined(activeIndex.value)
      ) {
        return inkDom.value[activeIndex.value];
      }
      return undefined;
    });

    const tabKeys = computed(() => sortedTabs.value.map((item) => item.key));

    const mergedPosition = computed(() => {
      return props.direction === "vertical" ? "left" : props.position;
    });

    const mergedDirection = computed(() =>
      ["left", "right"].includes(mergedPosition.value)
        ? "vertical"
        : "horizontal",
    );

    watch(
      () => props.position,
      () => {
        updateDom();
      },
    );

    const _activeKey = ref(props.defaultActiveKey);

    const computedActiveKey = computed(() => {
      const activeKey = props.activeKey ?? _activeKey.value;
      if (isUndefined(activeKey)) {
        return tabKeys.value[0];
      }
      return activeKey;
    });

    const activeIndex = computed(() => {
      const index = tabKeys.value.indexOf(computedActiveKey.value);
      if (index === -1) {
        return 0;
      }
      return index;
    });

    const handleChange = (key: string | number) => {
      if (key !== computedActiveKey.value) {
        _activeKey.value = key;
        emit("update:activeKey", key);
        emit("change", key);
      }
    };

    const handleClick = (item) => {
      if (!item.disabled) {
        handleChange(item.key);
        emit("tabClick", item.key);
        nextTick(() => {
          scrollIntoView();
        });
      }
    };

    function scrollIntoView() {
      let left = activeTabRef.value.left;
      let width = activeTabRef.value.width;
      let windowWidth = navRect.width;
      if (left > windowWidth / 2) {
        scrollLeft.value = left - (windowWidth - width) / 2;
      } else {
        scrollLeft.value = 0;
      }
    }

    let navRect: elementSizeType = {
      left: 0,
      top: 0,
      bottom: 0,
      right: 0,
      width: 0,
      height: 0,
      dataset: {},
      id: "",
    };

    function updateDom() {
      nextTick(async () => {
        let boxLeft = 0;
        let boxTop = 0;
        await getRect(".arco-tabs-nav-tab", false, instance).then((rects) => {
          boxLeft = rects.left;
          boxTop = rects.top;
          navRect = rects as elementSizeType;
        });
        await getRect(".arco-tabs-tab", true, instance).then((rects) => {
          const NodesRef: any = rects.map((item) => {
            item.left = item.left - boxLeft;
            item.top = item.top - boxTop;
            return item;
          });
          inkDom.value = NodesRef;
        });
      });
    }

    let num = -1;

    const addItem = (id: number, data: any) => {
      // 增加 num 并生成新的 key
      const key = ++num;
      // 创建新的对象以避免直接修改传入的数据
      const newData = { ...data, key };
      // 将新数据添加到 tabMap
      tabMap.set(id, newData);
      updateDom();
      return key;
    };

    const data = reactive({
      addItem,
      activeKey: computedActiveKey,
    });

    provide(tabsInjectionKey, data);

    const contStyle = computed<CSSProperties>(() => {
      const style: CSSProperties = {};
      style["marginLeft"] = `-${activeIndex.value * 100}%`;
      return style;
    });

    const contentClass = computed(() => [
      `${prefixCls}-content-list`,
      {
        [`${prefixCls}-content-animation`]: props.animation,
      },
    ]);

    const cls = computed(() => [
      prefixCls,
      `${prefixCls}-${mergedDirection.value}`,
      `${prefixCls}-${mergedPosition.value}`,
      `${prefixCls}-type-${props.type}`,
      `${prefixCls}-size-${mergedSize.value}`,
      {
        [`${prefixCls}-justify`]: props.justify,
      },
    ]);

    function initTabClass(item) {
      return [
        `${prefixCls}-tab`,
        {
          [`${prefixCls}-tab-active`]: item.key === computedActiveKey.value,
          [`${prefixCls}-tab-closable`]: item.editable && item.closable,
          [`${prefixCls}-tab-disabled`]: item.disabled,
        },
      ];
    }

    const clsNav = computed(() => [
      `${prefixCls}-nav`,
      `${prefixCls}-nav-${mergedDirection.value}`,
      `${prefixCls}-nav-${mergedPosition.value}`,
      `${prefixCls}-nav-size-${props.size}`,
      `${prefixCls}-nav-type-${props.type}`,
    ]);

    return {
      prefixCls,
      cls,
      clsNav,
      initTabClass,
      tabKeys,
      mergedPosition,
      contStyle,
      contentClass,
      activeIndex,
      sortedTabs,
      tabsTabRef,
      scrollLeft,
      computedActiveKey,
      activeTabRef,
      handleClick,
      instance,
      mergedDirection,
    };
  },
};
</script>
